package com.baidu.mpks.security;


import com.baidu.mpks.common.RedisClient;
import com.baidu.mpks.filter.LoginFilter;
import com.baidu.mpks.filter.LoginMaxOneAccountFilter;
import com.baidu.mpks.filter.LoginMaxOnlineAccountFilter;
import com.baidu.mpks.filter.ShenFeiTokenFilter;
import com.baidu.mpks.user.service.UserService;
import com.github.fartherp.shiro.CodecType;
import com.github.fartherp.shiro.RedisCacheManager;
import com.github.fartherp.shiro.RedisSessionDAO;
import com.github.fartherp.shiro.RedisSessionListener;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.realm.CachingRealm;
import org.apache.shiro.session.SessionListener;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.redisson.api.RedissonClient;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

import javax.servlet.Filter;
import java.util.*;

@Configuration
public class ShiroConfig {
    @Autowired
    RedisClient redisClient;
    @Autowired
    private RedissonClient redissonClient;
    private static String host;
    private static String redirectUrl;
    private int CRYPT_TIMES = 1024;

    public ShiroConfig() {
    }

    @Value("${shenfei.host}")
    public void setHost(String host) {
        ShiroConfig.host = host;
    }

    @Value("${shenfei.redirectUrl}")
    public void setRedirectUrl(String redirectUrl) {
        ShiroConfig.redirectUrl = redirectUrl;
    }

    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName("md5");
        hashedCredentialsMatcher.setHashIterations(this.CRYPT_TIMES);
        return hashedCredentialsMatcher;
    }

    @Bean
    public ApplicationRealm customApplicationRealm() {
        ApplicationRealm applicationRealm = new ApplicationRealm();
        return applicationRealm;
    }

    @Bean(
            name = {"lifecycleBeanPostProcessor"}
    )
    public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    @Bean
    @DependsOn({"lifecycleBeanPostProcessor"})
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(this.securityManager());
        return authorizationAttributeSourceAdvisor;
    }

    @Bean
    public DefaultWebSecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        ApplicationRealm realm = this.customApplicationRealm();
        securityManager.setRealm(realm);
        RedisCacheManager cacheMgmr = this.cacheManager(this.redissonClient);
        securityManager.setCacheManager(cacheMgmr);
        securityManager.setSessionManager(this.sessionManager(cacheMgmr, realm));
        return securityManager;
    }

    @Bean
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager, LoginMaxOnlineAccountFilter loginMaxOnlineAccountFilter, SessionManager sessionManager, CacheManager cacheManager, UserService userService, @Value("${login.max.one.account:1}") int loginMaxOneAccount, @Value("${login.kickoff.last:false}") boolean kickoffLast) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        Map<String, Filter> filterMap = new HashMap();
        LoginMaxOneAccountFilter loginMaxOneAccountFilter = new LoginMaxOneAccountFilter();
        loginMaxOneAccountFilter.setSessionManager(sessionManager);
        loginMaxOneAccountFilter.setCache(cacheManager.getCache("shiro-activeSessionCache"));
        loginMaxOneAccountFilter.setMaxSession(loginMaxOneAccount);
        loginMaxOneAccountFilter.setKickoutLast(kickoffLast);
        ShenFeiTokenFilter shenFeiTokenFilter = new ShenFeiTokenFilter(userService);
        shenFeiTokenFilter.setHost(host);
        shenFeiTokenFilter.setRedirectUrl(redirectUrl);
        LoginFilter loginFilter = new LoginFilter();
        loginFilter.setRedirectUrl(redirectUrl);
        filterMap.put("shenFeiTokenFilter", shenFeiTokenFilter);
        filterMap.put("jwt", loginFilter);
        filterMap.put("limit_login_one_account", loginMaxOneAccountFilter);
        filterMap.put("limit_login_total", loginMaxOnlineAccountFilter);
        shiroFilterFactoryBean.setFilters(filterMap);
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        shiroFilterFactoryBean.setLoginUrl("/user/login");
        shiroFilterFactoryBean.setSuccessUrl("/");
        shiroFilterFactoryBean.setUnauthorizedUrl("/unauth");
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap();
        filterChainDefinitionMap.put("/api/accessHome", "shenFeiTokenFilter");
        filterChainDefinitionMap.put("/user/login", "anon,limit_login_one_account,limit_login_total");
        filterChainDefinitionMap.put("/user/security/getUserInfo", "anon,limit_login_one_account");
        filterChainDefinitionMap.put("/user/loginAccess", "anon");
        filterChainDefinitionMap.put("/**", "anon");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

    @Bean
    public SessionManager sessionManager(RedisCacheManager cacheMgmr, ApplicationRealm realm) {
        RedisSessionDAO sessionDao = this.redisSessionDAO(cacheMgmr);
        List<SessionListener> sessionListeners = new ArrayList();
        sessionListeners.add(this.sessionListener(sessionDao, realm));
        ShiroSessionManager mySessionManager = new ShiroSessionManager();
        mySessionManager.setSessionDAO(sessionDao);
        mySessionManager.setGlobalSessionTimeout(2592000000L);
        mySessionManager.setSessionListeners(sessionListeners);
        return mySessionManager;
    }

    @Bean
    public SessionListener sessionListener(SessionDAO redisSessionDAO, ApplicationRealm realm) {
        List<CachingRealm> realms = new ArrayList();
        realms.add(realm);
        return new RedisSessionListener(redisSessionDAO, realms);
    }

    @Bean
    public RedisCacheManager cacheManager(RedissonClient redisson) {
        return new RedisCacheManager(redisson, "shiro:cache:", 2592000000L, 1024, CodecType.JSON_JACKSON_CODEC, CodecType.JSON_JACKSON_CODEC);
    }

    @Bean
    public RedisSessionDAO redisSessionDAO(RedisCacheManager cacheManager) {
        return new RedisSessionDAO(cacheManager);
    }
}
